import {each, mix} from "@antv/util"
import {vec2} from "@antv/matrix-util";
// const {mix} = G6.Util;
export default function (G6) {

    G6.registerBehavior('itemAlign', {

        getDefaultCfg(){
            return {
                alignLineStyle: {stroke: '#FA8C16', lineWidth: 1},
                tolerance: 10,
                _alignLines: []
            }
        },

        getEvents() {
            return {
                'afternodedrag': 'onDrag',
                'afternodedragend': 'onDragEnd'
            }
        },

        onDrag(shape){
            this._clearAlignLine();
            this._itemAlign(shape);
        },

        onDragEnd(){
            this._clearAlignLine();
        },

        _itemAlign(item) {
            const bbox = item.getBBox();
            const ct = {x: bbox.x + bbox.width / 2 , y: bbox.y};
            const cc = {x: bbox.x + bbox.width / 2, y: bbox.y + bbox.height / 2};
            const cb = {x: bbox.x + bbox.width / 2, y: bbox.y + bbox.height};
            const lc = {x: bbox.x, y:bbox.y + bbox.height / 2};
            const rc = {x: bbox.x + bbox.width, y: bbox.y + bbox.height / 2};
            const nodes = item._attrs && item._attrs.nodeId ? this.graph.getNodes().filter(n => n.get("id") !== item._attrs.nodeId) : this.graph.getNodes();
            each(nodes, (node) => {
                const horizontalLines = [];
                const verticalLines = [];
                let p = null;
                const bbox1  = node.getBBox();
                each(this.getHorizontalLines(bbox1), (line) => {
                    horizontalLines.push(this.getDistance(line, ct));
                    horizontalLines.push(this.getDistance(line, cc));
                    horizontalLines.push(this.getDistance(line, cb));
                })

                each(this.getVerticalLines(bbox1), (line) => {
                    verticalLines.push(this.getDistance(line, lc));
                    verticalLines.push(this.getDistance(line, cc));
                    verticalLines.push(this.getDistance(line, rc));
                })

                horizontalLines.sort((a, b) => a.dis - b.dis);
                verticalLines.sort((a, b) => a.dis - b.dis);
                if(horizontalLines.length > 0 && horizontalLines[0].dis < this.tolerance){
                    item.attr({y: horizontalLines[0].line[1] - horizontalLines[0].point.y + bbox.y});
                    p = {horizontals: [horizontalLines[0]]}
                    for (let i=1; i< 3; i++) horizontalLines[0].dis === horizontalLines[i].dis && p.horizontals.push(horizontalLines[i]);
                }
                if(verticalLines.length > 0 && verticalLines[0].dis < this.tolerance){
                    item.attr({y: verticalLines[0].line[1] - verticalLines[0].point.y + bbox.y});
                    p ? p.verticals = [verticalLines[0]] : p = {verticals: [verticalLines[0]] };
                    for (let i=1; i< 3; i++) verticalLines[0].dis === verticalLines[i].dis && p.verticals.push(verticalLines[i]);
                }
                if(p){
                    p.bbox = bbox;
                    this._addAlignLine(p);
                }
            })
        },

        _addAlignLine(p) {
            const group = this.graph.get("group");
            const bbox = p.bbox;
            const lineStyle = this.alignLineStyle;
            const lineArr = this._alignLines;
            if(p.horizontals){
                each(p.horizontals, lineObj => {
                    const line = lineObj.line;
                    const point = lineObj.point;
                    const lineHalf = (line[0] + line[2]) / 2;
                    let x1, x2;
                    if(point.x < lineHalf){
                        x1 = point.x + bbox.width / 2;
                        x2 = Math.max(line[0], line[2]);
                    }else{
                        x1 = point.x + bbox.width / 2;
                        x2 = Math.min(line[0], line[2]);
                    }
                    const shape = group.addShape('line', {attrs: mix({x1, y1: line[1], x2, y2: line[1]}, lineStyle), capture: false});
                    lineArr.push(shape);
                })
            }
            if(p.verticals){
                each(p.verticals, lineObj => {
                    const line = lineObj.line;
                    const point = lineObj.point;
                    const lineHalf = (line[1] + line[3]) / 2;
                    let y1, y2;
                    if(point.x < lineHalf){
                        y1 = point.y + bbox.height / 2;
                        y2 = Math.max(line[1], line[3]);
                    }else{
                        y1 = point.y + bbox.height / 2;
                        y2 = Math.min(line[1], line[3]);
                    }
                    const shape = group.addShape('line', {attrs: mix({x1: line[0], y1, x2: line[0], y2}, lineStyle), capture: false});
                    lineArr.push(shape);
                })
            }
        },

        getHorizontalLines(bbox) {
            return [
                [bbox.minX, bbox.minY, bbox.maxX, bbox.minY],
                [bbox.minX, bbox.centerY, bbox.maxX, bbox.centerY],
                [bbox.minX, bbox.maxY, bbox.maxX, bbox.maxY],
            ]
        },

        getVerticalLines(bbox) {
            return [
                [bbox.minX, bbox.minY, bbox.minX, bbox.maxY],
                [bbox.centerX, bbox.minY, bbox.centerX, bbox.maxY],
                [bbox.maxX, bbox.minY, bbox.maxX, bbox.maxY],
            ]
        },

        getDistance(line, point){
            return {line, point, dis: this.pointLineDistance(line[0], line[1], line[2], line[3], point.x, point.y) };
        },

        pointLineDistance(lineX1, lineY1, lineX2, lineY2, pointX, pointY){
            const lineLength = [lineX2 - lineX1, lineY2 - lineY1];
            if(vec2.exactEquals(lineLength, [0, 0])) return NaN;
            let s = [-lineLength[1], lineLength[0]];
            vec2.normalize(s, s);
            return Math.abs(vec2.dot([pointX - lineX1, pointY - lineY1], s));
        },

        _clearAlignLine(){
            each(this._alignLines, (line) => {
                line.remove();
            })
            this._alignLines = [];
            this.graph.paint();
        }
    })
}